package com.core.cloudcommon.security;

import com.core.cloudcommon.CommonCustomException;
import com.core.cloudcommon.CommonInteger;
import com.core.cloudcommon.redis.service.CloudRedisTemplateService;
import com.core.cloudcommon.redis.vo.CloudRedisVO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;
import java.util.Objects;

/**
 * @author =====Lqj=====
 * @className LoginSecurityManager
 * @description 登录安全管理器
 * @create 2024/12/16 10:30
 **/
@Slf4j
@Component
public class LoginSecurityManager {

    @Autowired
    private CloudRedisTemplateService cloudRedisTemplateService;

    // Redis Key前缀
    private static final String LOGIN_FAIL_KEY = "login_fail:";
    private static final String SMS_VERIFY_FAIL_KEY = "sms_verify_fail:";
    private static final String IP_FAIL_KEY = "ip_fail:";
    private static final String RATE_LIMIT_KEY = "rate_limit:";

    // 配置参数
    private static final int MAX_FAIL_COUNT = 5;           // 最大失败次数
    private static final long LOCK_TIME = 30 * 60L;       // 锁定时间30分钟
    private static final int MAX_SMS_VERIFY_FAIL = 3;     // 短信验证最大失败次数
    private static final int MAX_IP_FAIL_COUNT = 10;      // IP最大失败次数
    private static final long IP_LOCK_TIME = 60;    // IP锁定时间1

    /**
     * 检查账户是否被锁定
     *
     * @param account 账户（用户名或手机号）
     * @throws CommonCustomException 账户被锁定时抛出异常
     */
    public void checkAccountLock(String account) {
        if (account == null || account.trim().isEmpty()) {
            return;
        }

        String key = LOGIN_FAIL_KEY + account;
        Object failCount = cloudRedisTemplateService.get(new CloudRedisVO(key));

        if (failCount != null) {
            int count = Integer.parseInt(failCount.toString());
            if (count >= MAX_FAIL_COUNT) {
                Long expireTime = cloudRedisTemplateService.getExpire(new CloudRedisVO(key));
                long minutes = expireTime / 60;
                throw new CommonCustomException(200018,
                        String.format("账户已被锁定，请%d分钟后重试", minutes > 0 ? minutes : 1));
            }
        }
    }

    /**
     * 记录登录失败
     *
     * @param account 账户
     */
    public void recordLoginFail(String account) {
        if (account == null || account.trim().isEmpty()) {
            return;
        }

        String key = LOGIN_FAIL_KEY + account;
        Object failCount = cloudRedisTemplateService.get(new CloudRedisVO(key));

        int count = failCount == null ? 1 : Integer.parseInt(failCount.toString()) + 1;
        cloudRedisTemplateService.set(new CloudRedisVO(key, String.valueOf(count), LOCK_TIME));

        log.warn("账户 {} 登录失败，当前失败次数: {}", account, count);

        if (count >= MAX_FAIL_COUNT) {
            log.warn("账户 {} 因失败次数过多被锁定", account);
        }
    }

    /**
     * 清除登录失败记录（登录成功时调用）
     *
     * @param account 账户
     */
    public void clearLoginFail(String account) {
        if (account == null || account.trim().isEmpty()) {
            return;
        }

        String key = LOGIN_FAIL_KEY + account;
        cloudRedisTemplateService.del(key);
    }

    /**
     * 检查短信验证失败次数
     *
     * @param phone 手机号
     * @throws CommonCustomException 失败次数过多时抛出异常
     */
    public void checkSmsVerifyLimit(String phone) {
        if (phone == null || phone.trim().isEmpty()) {
            return;
        }

        String key = SMS_VERIFY_FAIL_KEY + phone;
        Object failCount = cloudRedisTemplateService.get(new CloudRedisVO(key));

        if (failCount != null) {
            int count = Integer.parseInt(failCount.toString());
            if (count >= MAX_SMS_VERIFY_FAIL) {
                throw new CommonCustomException(200019,
                        "验证码错误次数过多，请重新获取验证码");
            }
        }
    }

    /**
     * 记录短信验证失败
     *
     * @param phone 手机号
     */
    public void recordSmsVerifyFail(String phone) {
        if (phone == null || phone.trim().isEmpty()) {
            return;
        }

        String key = SMS_VERIFY_FAIL_KEY + phone;
        Object failCount = cloudRedisTemplateService.get(new CloudRedisVO(key));

        int count = failCount == null ? 1 : Integer.parseInt(failCount.toString()) + 1;
        cloudRedisTemplateService.set(new CloudRedisVO(key, String.valueOf(count), 300L)); // 5分钟过期

        log.warn("手机号 {} 验证码验证失败，当前失败次数: {}", phone, count);
    }

    /**
     * 清除短信验证失败记录
     *
     * @param phone 手机号
     */
    public void clearSmsVerifyFail(String phone) {
        if (phone == null || phone.trim().isEmpty()) {
            return;
        }

        String key = SMS_VERIFY_FAIL_KEY + phone;
        cloudRedisTemplateService.del(key);
    }

    /**
     * 检查IP是否被锁定
     *
     * @param ip IP地址
     * @throws CommonCustomException IP被锁定时抛出异常
     */
    public void checkIpLock(String ip) {
        if (ip == null || ip.trim().isEmpty()) {
            return;
        }

        String key = IP_FAIL_KEY + ip;
        Object failCount = cloudRedisTemplateService.get(new CloudRedisVO(key));

        if (failCount != null) {
            int count = Integer.parseInt(failCount.toString());
            if (count >= MAX_IP_FAIL_COUNT) {
                Long expireTime = cloudRedisTemplateService.getExpire(new CloudRedisVO(key));
                long minutes = expireTime / 60;
                throw new CommonCustomException(200020,
                        String.format("IP地址已被锁定，请%d分钟后重试", minutes > 0 ? minutes : 1));
            }
        }
    }

    /**
     * 记录IP登录失败
     *
     * @param ip IP地址
     */
    public void recordIpFail(String ip) {
        if (ip == null || ip.trim().isEmpty()) {
            return;
        }

        String key = IP_FAIL_KEY + ip;
        Object failCount = cloudRedisTemplateService.get(new CloudRedisVO(key));

        int count = failCount == null ? 1 : Integer.parseInt(failCount.toString()) + 1;
        cloudRedisTemplateService.set(new CloudRedisVO(key, String.valueOf(count), IP_LOCK_TIME));

        log.warn("IP {} 登录失败，当前失败次数: {}", ip, count);
    }

    /**
     * 接口限流检查
     *
     * @param ip          IP地址
     * @param api         接口名称
     * @param maxRequests 最大请求次数
     * @param timeWindow  时间窗口（秒）
     * @return true-允许访问，false-超出限制
     */
    public boolean checkRateLimit(String ip, String api, int maxRequests, int timeWindow) {
        if (ip == null || api == null) {
            return true;
        }

        String key = RATE_LIMIT_KEY + ip + ":" + api;
        Object count = cloudRedisTemplateService.get(new CloudRedisVO(key));

        if (count == null) {
            cloudRedisTemplateService.set(new CloudRedisVO(key, "1", (long) timeWindow));
            return true;
        }

        int currentCount = Integer.parseInt(count.toString());
        if (currentCount >= maxRequests) {
            log.warn("IP {} 访问接口 {} 超出限制，当前次数: {}", ip, api, currentCount);
            return false;
        }

        // 增加计数
        Long expireTime = cloudRedisTemplateService.getExpire(new CloudRedisVO(key));
        cloudRedisTemplateService.set(new CloudRedisVO(key, String.valueOf(currentCount + 1), expireTime));
        return true;
    }

    /**
     * 获取客户端IP地址
     *
     * @param request HTTP请求
     * @return IP地址
     */
    public String getClientIp(HttpServletRequest request) {
        if (request == null) {
            return "unknown";
        }

        String ip = request.getHeader("X-Forwarded-For");
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("WL-Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("HTTP_CLIENT_IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("HTTP_X_FORWARDED_FOR");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddr();
        }

        // 如果是多个IP，取第一个
        if (ip != null && ip.contains(",")) {
            ip = ip.split(",")[0].trim();
        }

        return ip;
    }
}
